#ifdef __STDC_VERSION__
#if __STDC_VERSION__ < 199901L
#error Rok 1999 już dawno za nami. Czas uaktualnić kompilator!
#endif
#else
#error Rok 1999 już dawno za nami. Czas uaktualnić kompilator!
#endif

/* Ten plik to tylko interfejs użytkownika.
 * Właściwy kod implementujący kryptografię
 * znajduje się w pliku przeksztalcenia.c
 */

#include<stdlib.h>
#include<stdbool.h>
#include<string.h>
#include<inttypes.h>
#include<gtk/gtk.h>
#include"przeksztalcenia.h"

GtkWidget *okno;
GtkWidget *panel;
GtkWidget *h1;
GtkWidget *h2;
GtkWidget *szyfruj1;
GtkWidget *szyfruj2;
GtkWidget *deszyfruj1;
GtkWidget *deszyfruj2;
GtkWidget *klucz;
GtkWidget *tekst1;
GtkWidget *tekst2;
GtkWidget *separator;
GtkTextBuffer *bufork;
GtkTextBuffer *bufor1;
GtkTextBuffer *bufor2;
GtkWidget *wejscie;
GtkWidget *wyjscie;
char* input=NULL;
char* output=NULL;

void szyfruj1_clicked(GtkButton *obiekt, gpointer dane)
{
   GtkTextIter pbufork, kbufork, pbufor1, kbufor1;
   GtkWidget * dialog;
   char *klucz, *tekst, *kod;
   char temp[18];
   uint_fast64_t k1=0, k2=0, k3=0, o=0;
   unsigned int i,j,bloki;
   size_t dlugosc;
   gtk_text_buffer_get_start_iter(bufork,&pbufork);
   gtk_text_buffer_get_end_iter(bufork,&kbufork);
   gtk_text_buffer_get_start_iter(bufor1,&pbufor1);
   gtk_text_buffer_get_end_iter(bufor1,&kbufor1);
   klucz=gtk_text_buffer_get_text(bufork,&pbufork,&kbufork,FALSE);
   tekst=gtk_text_buffer_get_text(bufor1,&pbufor1,&kbufor1,FALSE);
   if(strlen(klucz)!=24)
   {
      dialog=gtk_message_dialog_new(GTK_WINDOW(okno),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_ERROR,GTK_BUTTONS_NONE,"Błędna ilość bajtów klucza. Wprowadź dokładnie 24 bajty.");
      gtk_widget_show_all(dialog);
      free(klucz);
      free(tekst);
      return;
   }
   for(i=0; i<8; ++i)
   {
      k1=k1*256+klucz[i];
   }
   for(i=8; i<16; ++i)
   {
      k2=k2*256+klucz[i];
   }
   for(i=16; i<24; ++i)
   {
      k3=k3*256+klucz[i];
   }
   dlugosc=strlen(tekst);
   bloki=dlugosc/8+((dlugosc%8==0)?0:1);
   kod=(char*)malloc(sizeof(char)*(bloki*8*2+bloki+1));
   kod[bloki*17]='\0';
   for(i=0;i<bloki;++i)
   {
      for(j=0;j<8;++j)
      {
	 if(8*i+j<dlugosc)
	 {
	    o=o*256+tekst[8*i+j];
	 }
	 else
	 {
	    o=o*256;
	 }
      }
      sprintf(temp,"%016" PRIxFAST64 "\n",szyfruj_des(k3,deszyfruj_des(k2,szyfruj_des(k1,o))));
      memcpy(&(kod[i*17]),temp,17);
   }
   gtk_text_buffer_set_text(bufor2,kod,-1);
   free(klucz);
   free(tekst);
   free(kod);
}

void deszyfruj1_clicked(GtkButton *obiekt, gpointer dane)
{
   GtkTextIter pbufork, kbufork, pbufor2, kbufor2;
   GtkWidget * dialog;
   char *klucz, *tekst, *kod;
   char temp[8];
   uint_fast64_t k1=0, k2=0, k3=0, o=0, wartosc;
   unsigned int i,j,bloki;
   size_t dlugosc;
   gtk_text_buffer_get_start_iter(bufork,&pbufork);
   gtk_text_buffer_get_end_iter(bufork,&kbufork);
   gtk_text_buffer_get_start_iter(bufor2,&pbufor2);
   gtk_text_buffer_get_end_iter(bufor2,&kbufor2);
   klucz=gtk_text_buffer_get_text(bufork,&pbufork,&kbufork,FALSE);
   kod=gtk_text_buffer_get_text(bufor2,&pbufor2,&kbufor2,FALSE);
   if(strlen(klucz)!=24)
   {
      dialog=gtk_message_dialog_new(GTK_WINDOW(okno),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_ERROR,GTK_BUTTONS_NONE,"Błędna ilość bajtów klucza. Wprowadź dokładnie 24 bajty.");
      gtk_widget_show_all(dialog);
      free(klucz);
      free(kod);
      return;
   }
   for(i=0; i<8; ++i)
   {
      k1=k1*256+klucz[i];
   }
   for(i=8; i<16; ++i)
   {
      k2=k2*256+klucz[i];
   }
   for(i=16; i<24; ++i)
   {
      k3=k3*256+klucz[i];
   }
   dlugosc=strlen(kod);
   bloki=dlugosc/17;
   tekst=(char*)malloc(sizeof(char)*(bloki*8+1));
   tekst[bloki*8]='\0';
   for(i=0;i<bloki;++i)
   {
      sscanf(kod+(i*17),"%16" SCNxFAST64  "\n",&wartosc);
      o=deszyfruj_des(k1,szyfruj_des(k2,deszyfruj_des(k3,wartosc)));
      for(j=0;j<8;++j)
      {
	 temp[7-j]=o%256;
	 o=o/256;
      }
      memcpy(&(tekst[i*8]),temp,8);
   }
   gtk_text_buffer_set_text(bufor1,tekst,-1);
   free(klucz);
   free(tekst);
   free(kod);
}

void szyfruj2_clicked(GtkButton *obiekt, gpointer dane)
{
   GtkTextIter pbufork, kbufork;
   GtkWidget * dialog;
   char *klucz;
   uint_fast64_t k1=0, k2=0, k3=0, o=0;
   unsigned int i,j;
   FILE *wejscie, *wyjscie;
   bool jeszcze;
   unsigned char bufor[8];
   size_t ile;
   
   gtk_text_buffer_get_start_iter(bufork,&pbufork);
   gtk_text_buffer_get_end_iter(bufork,&kbufork);
   klucz=gtk_text_buffer_get_text(bufork,&pbufork,&kbufork,FALSE);
   if(strlen(klucz)!=24)
   {
      dialog=gtk_message_dialog_new(GTK_WINDOW(okno),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_ERROR,GTK_BUTTONS_NONE,"Błędna ilość bajtów klucza. Wprowadź dokładnie 24 bajty.");
      gtk_widget_show_all(dialog);
      free(klucz);
      return;
   }
   for(i=0; i<8; ++i)
   {
      k1=k1*256+klucz[i];
   }
   for(i=8; i<16; ++i)
   {
      k2=k2*256+klucz[i];
   }
   for(i=16; i<24; ++i)
   {
      k3=k3*256+klucz[i];
   }

   wejscie=fopen(input,"r");
   wyjscie=fopen(output,"w");
   if(wejscie==NULL || wejscie==NULL)
   {
      dialog=gtk_message_dialog_new(GTK_WINDOW(okno),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_ERROR,GTK_BUTTONS_NONE,"Błąd przy otwieraniu pliku.");
      gtk_widget_show_all(dialog);
      free(klucz);
      return;
   }
   jeszcze=true;
   while(jeszcze)
   {
      ile=fread(bufor,sizeof(unsigned char),8,wejscie);
      if(ile==0)
	 break;
      if(ile!=8)
	 jeszcze=false;
      o=0;
      for(i=0;i<8;++i)
      {
	 if(ile!=0)
	 {
	    o=o*256+bufor[i];
	 }
	 else
	 {
	    o=o*256;
	 }
	 ile=(ile==0?0:ile-1);
      }
      o=szyfruj_des(k3,deszyfruj_des(k2,szyfruj_des(k1,o)));
      for(i=0;i<8;++i)
      {
	 bufor[7-i]=o%256;
	 o=o/256;
      }
      ile=fwrite(bufor,sizeof(unsigned char),8,wyjscie);
      if(ile!=8)
      {
	 dialog=gtk_message_dialog_new(GTK_WINDOW(okno),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_ERROR,GTK_BUTTONS_NONE,"Błąd zapisu.");
	 gtk_widget_show_all(dialog);
	 break;
      }
   }
   fclose(wejscie);
   fclose(wyjscie);
   free(klucz);
}

void deszyfruj2_clicked(GtkButton *obiekt, gpointer dane)
{
   GtkTextIter pbufork, kbufork;
   GtkWidget * dialog;
   char *klucz;
   uint_fast64_t k1=0, k2=0, k3=0, o=0;
   unsigned int i,j;
   FILE *wejscie, *wyjscie;
   bool jeszcze;
   unsigned char bufor[8];
   size_t ile;
   
   gtk_text_buffer_get_start_iter(bufork,&pbufork);
   gtk_text_buffer_get_end_iter(bufork,&kbufork);
   klucz=gtk_text_buffer_get_text(bufork,&pbufork,&kbufork,FALSE);
   if(strlen(klucz)!=24)
   {
      dialog=gtk_message_dialog_new(GTK_WINDOW(okno),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_ERROR,GTK_BUTTONS_NONE,"Błędna ilość bajtów klucza. Wprowadź dokładnie 24 bajty.");
      gtk_widget_show_all(dialog);
      free(klucz);
      return;
   }
   for(i=0; i<8; ++i)
   {
      k1=k1*256+klucz[i];
   }
   for(i=8; i<16; ++i)
   {
      k2=k2*256+klucz[i];
   }
   for(i=16; i<24; ++i)
   {
      k3=k3*256+klucz[i];
   }

   wejscie=fopen(input,"r");
   wyjscie=fopen(output,"w");
   if(wejscie==NULL || wejscie==NULL)
   {
      dialog=gtk_message_dialog_new(GTK_WINDOW(okno),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_ERROR,GTK_BUTTONS_NONE,"Błąd przy otwieraniu pliku.");
      gtk_widget_show_all(dialog);
      free(klucz);
      return;
   }
   jeszcze=true;
   while(jeszcze)
   {
      ile=fread(bufor,sizeof(unsigned char),8,wejscie);
      if(ile==0)
	 break;
      if(ile!=8)
	 jeszcze=false;
      o=0;
      for(i=0;i<8;++i)
      {
	 if(ile!=0)
	 {
	    o=o*256+bufor[i];
	 }
	 else
	 {
	    o=o*256;
	 }
	 ile=(ile==0?0:ile-1);
      }
      o=deszyfruj_des(k1,szyfruj_des(k2,deszyfruj_des(k3,o)));
      for(i=0;i<8;++i)
      {
	 bufor[7-i]=o%256;
	 o=o/256;
      }
      ile=fwrite(bufor,sizeof(unsigned char),8,wyjscie);
      if(ile!=8)
      {
	 dialog=gtk_message_dialog_new(GTK_WINDOW(okno),GTK_DIALOG_DESTROY_WITH_PARENT,GTK_MESSAGE_ERROR,GTK_BUTTONS_NONE,"Błąd zapisu.");
	 gtk_widget_show_all(dialog);
	 break;
      }
   }
   fclose(wejscie);
   fclose(wyjscie);
   free(klucz);
}

void wejscie_clicked(GtkButton *obiekt, gpointer dane)
{
   GtkWidget *pliki;
   pliki=gtk_file_chooser_dialog_new(NULL,GTK_WINDOW(okno),GTK_FILE_CHOOSER_ACTION_OPEN,GTK_STOCK_OPEN,GTK_RESPONSE_ACCEPT,GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,NULL);
   if( gtk_dialog_run(GTK_DIALOG(pliki)) == GTK_RESPONSE_ACCEPT)
   {
      if(input!=NULL)
	 free(input);
      input=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pliki));
      gtk_button_set_label(GTK_BUTTON(wejscie),input);
   }
   gtk_widget_destroy(pliki);
}

void wyjscie_clicked(GtkButton *obiekt, gpointer dane)
{
   GtkWidget *pliki;
   pliki=gtk_file_chooser_dialog_new(NULL,GTK_WINDOW(okno),GTK_FILE_CHOOSER_ACTION_SAVE,GTK_STOCK_SAVE,GTK_RESPONSE_ACCEPT,GTK_STOCK_CANCEL,GTK_RESPONSE_CANCEL,NULL);
   if( gtk_dialog_run(GTK_DIALOG(pliki)) == GTK_RESPONSE_ACCEPT)
   {
      if(output!=NULL)
	 free(output);
      output=gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(pliki));
      gtk_button_set_label(GTK_BUTTON(wyjscie),output);
   }
   gtk_widget_destroy(pliki);
}

int main(int argc, char *argv[])
{
   gtk_init(&argc,&argv);

   okno=gtk_window_new(GTK_WINDOW_TOPLEVEL);
   g_signal_connect(okno,"destroy",G_CALLBACK(gtk_main_quit),NULL);

   panel=gtk_hpaned_new();
   gtk_container_add(GTK_CONTAINER(okno),panel);

   h1=gtk_vbox_new(FALSE,0);
   gtk_paned_pack1(GTK_PANED(panel),h1,TRUE,TRUE);

   h2=gtk_vbox_new(FALSE,0);
   gtk_paned_pack2(GTK_PANED(panel),h2,TRUE,TRUE);

   klucz=gtk_text_view_new();
   bufork=gtk_text_view_get_buffer(GTK_TEXT_VIEW(klucz));
   gtk_text_buffer_set_text(bufork,"Tu wpisz tekst klucza",-1);
   gtk_box_pack_start(GTK_BOX(h1),klucz,TRUE,TRUE,0);

   separator=gtk_hseparator_new();
   gtk_box_pack_start(GTK_BOX(h1),separator,TRUE,TRUE,0);

   tekst1=gtk_text_view_new();
   bufor1=gtk_text_view_get_buffer(GTK_TEXT_VIEW(tekst1));
   gtk_text_buffer_set_text(bufor1,"Tu wpisz tekst do zaszyfrowania",-1);
   gtk_box_pack_start(GTK_BOX(h1),tekst1,TRUE,TRUE,0);

   szyfruj1=gtk_button_new_with_label("szyfruj");
   g_signal_connect(szyfruj1,"clicked",G_CALLBACK(szyfruj1_clicked),NULL);
   gtk_box_pack_start(GTK_BOX(h1),szyfruj1,TRUE,TRUE,0);

   deszyfruj1=gtk_button_new_with_label("deszyfruj");
   g_signal_connect(deszyfruj1,"clicked",G_CALLBACK(deszyfruj1_clicked),NULL);
   gtk_box_pack_start(GTK_BOX(h1),deszyfruj1,TRUE,TRUE,0);

   tekst2=gtk_text_view_new();
   bufor2=gtk_text_view_get_buffer(GTK_TEXT_VIEW(tekst2));
   gtk_box_pack_start(GTK_BOX(h1),tekst2,TRUE,TRUE,0);

   wejscie=gtk_button_new_with_label("wybierz plik wejściowy");
   g_signal_connect(wejscie,"clicked",G_CALLBACK(wejscie_clicked),NULL);
   gtk_box_pack_start(GTK_BOX(h2),wejscie,TRUE,TRUE,0);

   wyjscie=gtk_button_new_with_label("wybierz plik wyjściowy");
   g_signal_connect(wyjscie,"clicked",G_CALLBACK(wyjscie_clicked),NULL);
   gtk_box_pack_start(GTK_BOX(h2),wyjscie,TRUE,TRUE,0);

   szyfruj2=gtk_button_new_with_label("szyfruj");
   g_signal_connect(szyfruj2,"clicked",G_CALLBACK(szyfruj2_clicked),NULL);
   gtk_box_pack_start(GTK_BOX(h2),szyfruj2,TRUE,TRUE,0);

   deszyfruj2=gtk_button_new_with_label("deszyfruj");
   g_signal_connect(deszyfruj2,"clicked",G_CALLBACK(deszyfruj2_clicked),NULL);
   gtk_box_pack_start(GTK_BOX(h2),deszyfruj2,TRUE,TRUE,0);

   gtk_widget_show_all(okno);
   gtk_main();
   return EXIT_SUCCESS;
}
